using System;
using System.IO;
using System.Text;
using System.Reflection;
using System.Drawing;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Soap;
using System.Runtime.Serialization.Formatters.Binary;

using Crainiate.Data.Persistence;
using Crainiate.Data.Persistence.Cache;
using Crainiate.Data.Persistence.Providers.SqlServer;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Crainiate.Data.Persistence.Testing
{
    [TestClass]
    public class SerializationTest
    {
        public SerializationTest()
        {

        }

        [TestMethod]
        public void SerializeResults()
        {
            Results<Customer> results = new Results<Customer>();

            Customer customer = new Customer();
            customer.Id = 1;
            customer.LastNames = "Bob";
            customer.CreditLimit = 500;
            results.Add(customer);

            customer = new Customer();
            customer.Id = 2;
            customer.LastNames = "John";
            customer.CreditLimit = 1000;
            results.Add(customer);

            //Format as soap to a memory stream
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Stream stream = new MemoryStream();
            formatter.Serialize(stream, results);

            //Load from stream
            stream.Position = 0;
            formatter = new BinaryFormatter();
            Results<Customer> newresults = formatter.Deserialize(stream) as Results<Customer>;

            Assert.IsTrue(newresults.Count == results.Count,"Deserialized result counts do not match");

            int count = 0;
            foreach (Customer newresult in newresults)
            {
                Assert.IsTrue(newresult.Id == results[count].Id, "Deserialized ids do not match");
                Assert.IsTrue(newresult.CreditLimit == results[count].CreditLimit, "Deserialized credit limits do not match");
                count++;
            }
        }

        [TestMethod]
        public void SerializeConcurrency()
        {
            Concurrency concurrency = new Concurrency();
            DateTime date = DateTime.Now;

            concurrency.Enabled = false;
            concurrency.Status = ConcurrencyStatus.Updated;

            concurrency.Add("int", 1);
            concurrency.Add("string", "two");
            concurrency.Add("date", date);

            //Format as binary to a memory stream
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Stream stream = new MemoryStream();
            formatter.Serialize(stream, concurrency);

            //Load from stream
            stream.Position = 0;
            formatter = new BinaryFormatter();
            Concurrency newconcurrency = formatter.Deserialize(stream) as Concurrency;

            Assert.IsTrue(newconcurrency.Enabled == concurrency.Enabled, "Deserialized concurrency Enabled does not match");
            Assert.IsTrue(newconcurrency.Status == concurrency.Status, "Deserialized concurrency Status does not match");
            Assert.IsTrue(newconcurrency.Count == concurrency.Count, "Deserialized concurrency item count does not match");

            Assert.IsTrue(newconcurrency["int"].Equals(concurrency["int"]), "Deserialized concurrency 'int' does not match");
            Assert.IsTrue(newconcurrency["string"].Equals(concurrency["string"]), "Deserialized concurrency 'string' does not match");
            Assert.IsTrue(newconcurrency["date"].Equals(concurrency["date"]), "Deserialized concurrency 'date' does not match");
        }

        [TestMethod]
        public void SerializeTable()
        {
            Customer customer = new Customer();
            customer.Id = 3;
            customer.FirstNames = "Jimmy";
            customer.CreditLimit = 9000;

            //Set a new context so that concurrency can be created
            customer.Concurrency = new Concurrency();

            Concurrency concurrency = customer.Concurrency;
            concurrency.Add("Id", customer.Id);
            concurrency.Add("FirstNames", customer.FirstNames);
            concurrency.Add("CreditLimit", customer.CreditLimit);

            //Format as binary to a memory stream
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Stream stream = new MemoryStream();
            formatter.Serialize(stream, customer);

            //Load from stream
            stream.Position = 0;
            formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Customer customer2 = formatter.Deserialize(stream) as Customer;

            Assert.IsTrue(customer2.Id == 3, "Deserialized Customer Id not correct.");
            Assert.IsTrue(customer2.CreditLimit == 9000, "Deserialized Customer CreditLimit not correct.");
        }

        [TestMethod]
        public void SerializeView()
        {
            CustomerView view = new CustomerView();
            view.Id = 3;
            view.FirstNames = "Jimmy";
            view.CreditLimit = 9000;

            //Set a new context so that concurrency can be created
            view.Concurrency = new Concurrency();

            Concurrency concurrency = view.Concurrency;
            concurrency.Add("Id", view.Id);
            concurrency.Add("FirstNames", view.FirstNames);
            concurrency.Add("CreditLimit", view.CreditLimit);

            //Format as binary to a memory stream
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Stream stream = new MemoryStream();
            formatter.Serialize(stream, view);

            //Load from stream
            stream.Position = 0;
            formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            CustomerView view2 = formatter.Deserialize(stream) as CustomerView;

            Assert.IsTrue(view2.Id == 3, "Deserialized Customer View Id not correct.");
            Assert.IsTrue(view2.CreditLimit == 9000, "Deserialized Customer View CreditLimit not correct.");
        }

        [TestMethod]
        public void ColumnTests()
        {
            Column column = new Column(typeof(Person), "FirstNames");

            BinaryFormatter formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Stream stream = new MemoryStream();
            formatter.Serialize(stream, column);

            stream.Position = 0;
            formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Column column2 = formatter.Deserialize(stream) as Column;

            Assert.AreEqual(column.PropertyInfo, column2.PropertyInfo, "Column object not reserialized correctly.");
        }

        [TestMethod]
        public void ColumnsTests()
        {
            Columns columns = new Columns(typeof(Customer), new string[] { "FirstNames", "CreditLimit" });

            BinaryFormatter formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Stream stream = new MemoryStream();
            formatter.Serialize(stream, columns);

            stream.Position = 0;
            formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Columns columns2 = formatter.Deserialize(stream) as Columns;

            Assert.AreEqual(columns.Count, columns2.Count, "Columns object not reserialized correctly.");
        }

        [TestMethod]
        public void ConditionTests()
        {
            Condition condition = new Condition(typeof(Person), "FirstNames", "Jim");

            BinaryFormatter formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Stream stream = new MemoryStream();
            formatter.Serialize(stream, condition);

            stream.Position = 0;
            formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Condition condition2 = formatter.Deserialize(stream) as Condition;

            Assert.AreEqual(condition.PropertyInfo, condition2.PropertyInfo, "Comparison object not reserialized correctly.");
            Assert.AreEqual(condition.Comparison, condition2.Comparison, "Comparison condition not reserialized correctly.");
            Assert.AreEqual(condition.Value, condition2.Value, "Comparison value not reserialized correctly.");
        }


        [TestMethod]
        public void ConditionsTests()
        {
            Conditions conditions = new Conditions();

            //Create some conditions
            conditions.Add(new Condition(typeof(Customer), "CreditLimit", "6000"));
            conditions.Add(new Condition(typeof(Customer), "FirstNames", "Jo", Comparison.StartsWith));

            BinaryFormatter formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Stream stream = new MemoryStream();
            formatter.Serialize(stream, conditions);

            stream.Position = 0;
            formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Conditions conditions2 = formatter.Deserialize(stream) as Conditions;

            Assert.AreEqual(conditions.Count, conditions2.Count, "Conditions object not reserialized correctly.");
            Assert.AreEqual(conditions.Operator, conditions2.Operator, "Conditions operator not reserialized correctly.");
        }

        [TestMethod]
        public void SubConditionsTests()
        {
            Conditions conditions = new Conditions();

            //Create some conditions with sub conditions
            conditions.Add(new Condition(typeof(Customer), "CreditLimit", "6000"));
            conditions.Add(new Condition(typeof(Customer), "FirstNames", "Jo", Comparison.StartsWith));

            Conditions subconditions = new Conditions();
            subconditions.Operator = Operator.Or;

            subconditions.Add(new Condition(typeof(Customer), "CreditLimit", "6000", Comparison.GreaterThan));
            subconditions.Add(new Condition(typeof(Customer), "CreditLimit", "9000", Comparison.LessThan));

            conditions.SubConditions = subconditions;

            BinaryFormatter formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Stream stream = new MemoryStream();
            formatter.Serialize(stream, conditions);

            stream.Position = 0;
            formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Conditions conditions2 = formatter.Deserialize(stream) as Conditions;

            Assert.AreEqual(conditions.Count, conditions2.Count, "Conditions object not reserialized correctly.");
            Assert.AreEqual(conditions.Operator, conditions2.Operator, "Conditions operator not reserialized correctly.");
            Assert.AreEqual(conditions.SubConditions.Count, conditions2.SubConditions.Count, "Conditions subconditions objects not reserialized correctly.");
            Assert.AreEqual(conditions.SubConditions.Operator, conditions2.SubConditions.Operator, "SubConditions operator not reserialized correctly.");
        }

        [TestMethod]
        public void OrderTest()
        {
            Crainiate.Data.Persistence.Order order = new Crainiate.Data.Persistence.Order(typeof(Person), "FirstNames", SortDirection.Descending);

            BinaryFormatter formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Stream stream = new MemoryStream();
            formatter.Serialize(stream, order);

            stream.Position = 0;
            formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Crainiate.Data.Persistence.Order order2 = formatter.Deserialize(stream) as Crainiate.Data.Persistence.Order;

            Assert.AreEqual(order.PropertyInfo, order2.PropertyInfo, "Order object not reserialized correctly.");
            Assert.AreEqual(order.Direction, order2.Direction, "Order direction not reserialized correctly.");
        }

        [TestMethod]
        public void RelationshipTest()
        {
            Customer customer = new Customer();
            CustomerAddress ca = new CustomerAddress();
            Address address = new Address();
            Relationship relationship = new Relationship(ca);

            customer.Id = 1;
            ca.CustomerId = 1;
            address.Id = 10;
            ca.AddressId = 10;

            relationship.Parents.Add(customer);
            relationship.Children.Add(address);

            BinaryFormatter formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Stream stream = new MemoryStream();
            formatter.Serialize(stream, relationship);

            stream.Position = 0;
            formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Relationship relationship2 = formatter.Deserialize(stream) as Relationship;

            CustomerAddress ca2 = relationship2.Result as CustomerAddress;
            Customer customer2 = relationship2.Parents[0] as Customer;
            Address address2 = relationship2.Children[0] as Address;

            Assert.IsNotNull(ca2, "Relationship result not serialized correctly.");
            Assert.IsNotNull(relationship2.Parents, "Relationship parents not serialized correctly.");
            Assert.IsNotNull(relationship2.Children, "Relationship children not serialized correctly.");

            Assert.AreEqual(ca.AddressId, ca2.AddressId, "Relationship result not serialized correctly.");
            Assert.AreEqual(ca.CustomerId, ca2.CustomerId, "Relationship result not serialized correctly.");
            Assert.AreEqual(customer.Id, customer2.Id, "Relationship parent not serialized correctly.");
            Assert.AreEqual(address.Id, address2.Id, "Relationship child result not serialized correctly.");

        }

        [TestMethod]
        public void RelationshipsTest()
        {
            Customer customer = new Customer();
            CustomerAddress ca = new CustomerAddress();
            Address address = new Address();
            Relationship relationship = new Relationship(ca);
            Relationships relationships = new Relationships();

            customer.Id = 1;
            ca.CustomerId = 1;
            address.Id = 10;
            ca.AddressId = 10;

            relationship.Parents.Add(customer);
            relationship.Children.Add(address);

            relationships.Add(ca, relationship);

            BinaryFormatter formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Stream stream = new MemoryStream();
            formatter.Serialize(stream, relationships);

            stream.Position = 0;
            formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Relationships relationships2 = formatter.Deserialize(stream) as Relationships;

            Assert.IsTrue(relationships2.Count == 1, "Relationships not serialized correctly.");
        }

        [TestMethod]
        public void StreamTest()
        {
            Note note = new Note();
            note.Id = 11;
            note.Description = "Test Note 11";

            Bitmap bitmap = new Bitmap(400, 400);
            Graphics graphic = Graphics.FromImage(bitmap);

            graphic.FillRectangle(new SolidBrush(Color.Blue), 0, 0, 400, 400);
            graphic.Dispose();

            note.Stream = new MemoryStream();
            bitmap.Save(note.Stream, System.Drawing.Imaging.ImageFormat.Bmp);

            //Format as binary to a memory stream
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Stream stream = new MemoryStream();
            formatter.Serialize(stream, note);

            //Load from stream
            stream.Position = 0;
            formatter = new BinaryFormatter();
            Note newNote = formatter.Deserialize(stream) as Note;

            bitmap = new Bitmap(newNote.Stream);

            Assert.IsTrue(newNote.Description == "Test Note 11", "Note not selected correctly");
            Assert.IsTrue(bitmap.Width == 400 && bitmap.Height == 400, "Note image not selected correctly.");
        }

        [TestMethod]
        public void QueryTest()
        {
            //Simple query with no subqueries
            Query<Customer> query = new Query<Customer>();
            query.Fill = true;

            Conditions conditions = new Conditions();

            //Create some conditions
            conditions.Add(new Condition(typeof(Customer), "CreditLimit", "6000"));
            conditions.Add(new Condition(typeof(Customer), "FirstNames", "Jo", Comparison.StartsWith));
            query.Where = conditions;

            query.OrderBy = new Columns(typeof(Customer), new string[] { "CreditLimit" });

            BinaryFormatter formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Stream stream = new MemoryStream();
            formatter.Serialize(stream, query);

            stream.Position = 0;
            formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Query<Customer> query2 = formatter.Deserialize(stream) as Query<Customer>;

            //check the values
            Assert.AreEqual(query.Fill, query2.Fill, "Query Fill property not reserialized correctly.");
            Assert.AreEqual(query.Where.Count, query2.Where.Count, "Query Conditions object not reserialized correctly.");
            Assert.AreEqual(query.Where.Operator, query2.Where.Operator, "Query Conditions operator not reserialized correctly.");

            Assert.AreEqual(query.OrderBy.Count, query2.OrderBy.Count, "Query OrderBy not reserialized correctly.");
        }

        [TestMethod]
        public void AdvancedQueryTest()
        {
            Query<Customer> query = new Query<Customer>();
            query.Fill = true;

            Conditions conditions = new Conditions();

            //Create some conditions
            conditions.Add(new Condition(typeof(Customer), "CreditLimit", "6000"));
            conditions.Add(new Condition(typeof(Customer), "FirstNames", "Jo", Comparison.StartsWith));
            query.Where = conditions;

            query.OrderBy = new Columns(typeof(Customer), new string[] { "CreditLimit" });

            //Add a join 
            Query<CustomerAddress> subquery = new Query<CustomerAddress>();
            query.Join(subquery, "Id", "CustomerId");

            BinaryFormatter formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Stream stream = new MemoryStream();
            formatter.Serialize(stream, query);

            stream.Position = 0;
            formatter = new BinaryFormatter();
            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Query<Customer> query2 = formatter.Deserialize(stream) as Query<Customer>;

            //check the values
            Assert.AreEqual(query.Fill, query2.Fill, "Query Fill property not reserialized correctly.");
            Assert.AreEqual(query.Where.Count, query2.Where.Count, "Query Conditions object not reserialized correctly.");
            Assert.AreEqual(query.Where.Operator, query2.Where.Operator, "Query Conditions operator not reserialized correctly.");

            Assert.AreEqual(query.OrderBy.Count, query2.OrderBy.Count, "Query OrderBy not reserialized correctly.");
            Assert.IsTrue(query2.SubQueries.Count == 1, "Query does not have a sub query.");
            Assert.IsTrue(query2.SubQueries[0].JoinFrom.Count == 1, "Query does not have a sub query join from field.");
            Assert.IsTrue(query2.SubQueries[0].JoinTo.Count == 1, "Query does not have a sub query join to field.");

            Assert.IsTrue(query2.SubQueries[0].JoinFrom[0].PropertyInfo.Name == "Id", "Query does not have correct sub query join from field.");
            Assert.IsTrue(query2.SubQueries[0].JoinTo[0].PropertyInfo.Name == "CustomerId", "Query does not have correct sub query join to field.");
        }
    }
}
